use std::sync::Arc;

use tauri::{Emitter, Window};
use tokio::{
    io::{split, AsyncReadExt, AsyncWriteExt, BufReader, ReadHalf, WriteHalf},
    sync::Semaphore,
};
use tokio_serial::{SerialPortBuilderExt, SerialStream};

use crate::{error::AppErr, utils::new_buf};

pub struct ConnPort {
    writer: Writer,
    exit_sem: Arc<Semaphore>,
}

type Writer = WriteHalf<SerialStream>;
type Reader = ReadHalf<SerialStream>;
type BuffedReader = BufReader<Reader>;

impl ConnPort {
    pub fn new(name: &str, baud_rate: u32, window: Window) -> Result<ConnPort, AppErr> {
        let stream = tokio_serial::new(name, baud_rate).open_native_async()?;
        let (r, w) = split(stream);
        let reader = BufReader::with_capacity(255, r);
        let exit_sem = Arc::new(Semaphore::new(0));
        tokio::spawn(read_task(reader, exit_sem.clone(), window));
        Ok(ConnPort {
            writer: w,
            exit_sem,
        })
    }

    pub async fn write(&mut self, buf: &[u8]) -> Result<(), AppErr> {
        self.writer.write(buf).await?;
        Ok(())
    }

    pub fn close(&self) {
        self.exit_sem.add_permits(1);
    }
}

async fn read_task(mut reader: BuffedReader, exit_sem: Arc<Semaphore>, window: Window) {
    tokio::select! {
        ret = read_loop(&mut reader, window) => {
            if let Err(e) = ret {
                println!("read err:{}", e);
            }
        }
        _ = exit_sem.acquire() => {
            println!("read exit");
        }
    }
}

const READ_EVENT: &'static str = "read/event";

async fn read_loop(reader: &mut BuffedReader, window: Window) -> Result<(), AppErr> {
    let mut buf_temp = new_buf(255);
    loop {
        let len = sync(reader).await?;
        if len < 7 {
            println!("invalid len:{}", len);
            continue;
        }
        let len = len - 3;
        let buf = &mut buf_temp[..len];
        reader.read_exact(buf).await?;
        let s = xor_sum(&buf[..(len - 2)]);
        if s != buf[len - 2] {
            println!("invalid check sum:{} {}", buf[len - 2], s);
            continue;
        }
        if END != buf[len - 1] {
            println!("invalid end:{}", END);
            continue;
        }
        _ = window.emit(READ_EVENT, &*buf);
    }
}

const HEAD0: u8 = 0xE1;
const HEAD1: u8 = 0x1E;
const END: u8 = 0xEF;

fn xor_sum(buf: &[u8]) -> u8 {
    let mut s = 0;
    for v in buf {
        s = s ^ *v;
    }
    s
}

async fn sync(reader: &mut BuffedReader) -> Result<usize, AppErr> {
    let mut flag = false;
    let mut buf = [0u8; 1];
    loop {
        reader.read_exact(&mut buf).await?;
        if flag && (buf[0] == HEAD1) {
            break;
        }
        flag = buf[0] == HEAD0;
    }
    reader.read_exact(&mut buf).await?;
    Ok(buf[0] as usize)
}
